#include <errno.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_mixer.h>

#define FONTNAME "tcc_intro_maker_9a.png"
#define MUSIC "universum.mod"

typedef struct {
  SDL_Surface *line;
  Sint16 width;
  float t;
  float A;
  float dt;
} Textline;

Mix_Music *music;
SDL_Surface *font;
SDL_Surface *screen;
Textline textlines[20];
Uint32 last_ticks;
float movementrate;
Uint32 sleep_until_ticks = 0;
int windowed_mode = 0;
const char *music_filename = MUSIC;
const char *font_filename = FONTNAME;

std::vector<std::string> introtext;
unsigned int introtextcounter = 0;

static struct option long_options[] = {
  { "music", required_argument, NULL, 'm' },
  { "font", required_argument, NULL, 'f' },
  { NULL }
};


void handlesignal(int signal) {
  SDL_Quit();
  exit(128 + signal);
}


void initrnd() {
  time_t starttime;

  if(time(&starttime) != -1) {
    srandom((unsigned int) starttime);
  } else {
    srandom(-0xdeadbea1);
  }
}


float rnd() {
  return (float) random() / (float) RAND_MAX;
}


/*
 * \return width of text in pixel
 */
Sint16 draw_text(int x, int y, const char *txt, SDL_Surface *target) {
  SDL_Rect srect = { 0, 0, 16, 16 };
  SDL_Rect drect = { x, y, 16, 16 };
  while(*txt) {
    int ch = tolower(*txt++);
    if(ch != ' ') {
      if(ch >= 0x60 && ch <= 0x7f) ch -= 0x60;
      srect.y = ch * 16;
      SDL_BlitSurface(font, &srect, target, &drect);
    }
    drect.x += 17;
  }
  return drect.x;
}

void fill_textlines() {
  short h;

  if(introtextcounter >= introtext.size()) {
    introtextcounter = 0; //Restart...
    sleep_until_ticks = last_ticks + 5935;
  }
  switch(introtext[introtextcounter][0]) {
  case '\f':
    if(std::istringstream(introtext[introtextcounter].substr(1)) >> h) {
      sleep_until_ticks = last_ticks + h;
    } else {
      sleep_until_ticks = last_ticks + 2935;
    }
    ++introtextcounter;
    break;
  default:
    break;
  }

  for(int i = 0; i < 20; ++i) {
    SDL_FillRect(textlines[i].line, NULL, SDL_MapRGB(textlines[i].line->format, 0xff, 0xff, 0xff));
    textlines[i].A = 0;
    textlines[i].width = 0;
  }
  for(int i = 0; i < 20; ++i) {
    SDL_Surface *surf = textlines[i].line;

    if(introtextcounter < introtext.size()) {
      const std::string &txt = introtext[introtextcounter];

      if(!txt.empty()) {
	if(txt[0] < ' ') break;
	textlines[i].width = draw_text(0, 0, txt.c_str(), surf);
	textlines[i].t = -(10 + 4 * i);
	textlines[i].A = 650 + rnd() * 200;
	textlines[i].dt = 0.035907 + rnd() * 0.2;
      }
      introtextcounter++;
    }
  }
}

bool display_textlines() {
  bool painted = false;
  float y = 9 * sin(last_ticks / 153.33) + 39;

  for(int i = 0; i < 20; ++i) {
    float A = textlines[i].A;
    SDL_Surface *surf = textlines[i].line;
    SDL_Rect rect = { 400 - textlines[i].width / 2, y + i * 20, 0, 0 };
    if(A > 0) {
      painted = true;
      if(textlines[i].t < 0) {
	textlines[i].t += movementrate;
	//End loop as drawing is delayed.
	continue;
      } else {
	textlines[i].t += textlines[i].dt * movementrate;
      }
      if((textlines[i].A *= 0.9379) < 1e-1) {
	textlines[i].A = 0;
      }
      rect.x -= cosf(textlines[i].t) * A;
    }
    if(textlines[i].t >= 0) SDL_BlitSurface(surf, NULL, screen, &rect);
  }
  return painted;
}


int main(int argc, char **argv) {
  SDL_Event event;
  int opt;
  int option_index = 0;
  bool running = 1;

  while ((opt = getopt_long(argc, argv, "f:m:w", long_options, &option_index)) != -1) {
    switch(opt) {
    case 'm':
      printf("music = %s\n", optarg);
      music_filename = strdup(optarg);
      break;
    case 'f':
      printf("font = %s\n", optarg);
      font_filename = strdup(optarg);
      break;
    case 'w':
      windowed_mode = 1;
      break;
    default:
      fprintf(stderr, "getopt returned: $%02x '%c'\n", opt, isprint(opt) ? opt : '?');
      return 7;
    }
  }
  if(optind >= argc) {
    fprintf(stderr, "Provide a filename!\n");
    return 7; 
  } else {
    std::string str;
    std::ifstream in(argv[optind]);
    while(getline(in, str)) introtext.push_back(str);
  }
  if(SDL_Init(SDL_INIT_EVERYTHING) != 0) {
    return 1;
  }
  signal(SIGINT, handlesignal);
  signal(SIGTERM, handlesignal);
  signal(SIGQUIT, handlesignal);
  atexit(SDL_Quit);
  if((screen = SDL_SetVideoMode(800, 480, 32, (windowed_mode ? 0 : SDL_FULLSCREEN) | SDL_DOUBLEBUF | SDL_HWSURFACE)) == NULL) {
    return 2;
  }
  if((font = IMG_Load(font_filename)) == NULL) {
    return 3;
  }
  SDL_ShowCursor(SDL_DISABLE);
  initrnd();
  for(int i = 0; i < 20; ++i) {
    SDL_Surface *surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 800, 16, 32, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
    if(surf == NULL) return 4;
    textlines[i].line = surf;
  }
  if(Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, AUDIO_S16, MIX_DEFAULT_CHANNELS, 1024) != 0) {
    return 5;
  }
  if((music = Mix_LoadMUS(music_filename)) == NULL) return 6;
  Mix_FadeInMusic(music, -1, 2720);
  fill_textlines();
  last_ticks = SDL_GetTicks();
  while(running) {
    Uint32 ticks = SDL_GetTicks();
    if(ticks >= sleep_until_ticks) {
      movementrate = (ticks - last_ticks) / 50.0;
      if(movementrate > 0) {
	last_ticks = ticks;
	SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0xff, 0xff, 0xff));
	if(!display_textlines()) {
	  fill_textlines();
	}
	SDL_Flip(screen);
	SDL_Delay(50);
      }
    }
    while(SDL_PollEvent(&event) != 0) {
      if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_KEYDOWN) running = 0;
    }
    last_ticks = ticks;
  }
  return 0;
}
